package tk.xboot.ws.conf;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * WebSocket server endpoint configurator.
 *
 * @since 1.0
 * @version 1.0
 * @author Asin Liu
 */

@Component
public class WsContextProvider extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
    private static volatile BeanFactory context;
    private static final transient String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";

    Map<String, String> userTokens;

    public WsContextProvider() {
        userTokens = new ConcurrentHashMap<>();
    }

    public Map<String, String> getUserTokens(){
        return Collections.unmodifiableMap(userTokens);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    @Override
    public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException {
        return context.getBean(clazz);
    }


    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }


    @Override
    public boolean checkOrigin(String originHeaderValue) {
        if(originHeaderValue==null || originHeaderValue.trim().length()==0)
            return false;
        return true;
        //return ORIGIN.equals(originHeaderValue);
    }

    /**
     * Global state
     * There are multiple options here as well. Please note that these are scoped to a specific Endpoint
     *
     * getUserProperties in EndpointConfig – it exposes the same Map interface as the one in Session.
     * Since the WebSocket runtime creates a single instance of an EndpointConfig object per Endpoint ,
     * it can be used a global state store
     * @param config
     * @param request
     * @param response
     */
    @Override
    public void modifyHandshake(ServerEndpointConfig config,
                                HandshakeRequest request, HandshakeResponse response) {

        try {
            SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        /*String token = request.getHeaders().get("token").get(0);
        String name = request.getUserPrincipal().getName();*/
        String name = "userName";
        String token = "getFromToken";
        userTokens.put(name, token);


       /* Map<String, List<String>> headers = request.getHeaders();
        if (headers != null && headers.containsKey(WatcherSecurityKey.HTTP_HEADER)) {
            List<String> header = headers.get(WatcherSecurityKey.HTTP_HEADER);
            if (!header.isEmpty()) {
                config.getUserProperties().put(WatcherSecurityKey.HTTP_HEADER, header.
                        get(0));
            }
        }
        HttpSession httpSession = (HttpSession) request.getHttpSession();
        String user = request.getUserPrincipal().getName();
        config.getUserProperties().put("httpSession", httpSession);
        config.getUserProperties().put("user", user);
        logger.log(Level.INFO, "Hand shake for upgrade to websocket by: {0}", user);*/


        /*HttpSession httpSession = (HttpSession) request.getHttpSession();
        ServletContext context = (ServletContext) httpSession.getServletContext();

        config.getUserProperties().put("httpSession", httpSession);
        config.getUserProperties().put("user", request.getUserPrincipal().getName());

        *//*
         * store these attributes to servletContext so that they are available to
         * every created user socket session
         *//*
        config.getUserProperties().put("protocol", context.getAttribute("protocol"));*/


        super.modifyHandshake(config, request, response);
        System.out.println(">>>>" +    Thread.currentThread().getId());

        List<String> sec = request.getHeaders().get(SEC_WEBSOCKET_PROTOCOL);
        String secProtocol="";
        if(sec != null && !sec.isEmpty())
            secProtocol = sec.get(0);

        System.out.println(secProtocol);

        HttpSession session = (HttpSession) request.getHttpSession();


        response.getHeaders().put(SEC_WEBSOCKET_PROTOCOL,  Arrays.asList(secProtocol));

        // Store tunnel request and tunnel request service for retrieval
        // upon WebSocket open
        Map<String, Object> properties = config.getUserProperties();
        properties.clear();
        properties.put("token", request.getHeaders().get("x-tk"));
        properties.put("Author", "Asin Liu");
    }


    /**
     * Return locale of client
     *
     * @param request
     * @return
     */
    Locale getLocale(HandshakeRequest request) {
        if(null != request) {
            Map<String, List<String>> headers = request.getHeaders();
            if(null != headers) {
                List<String> accepts = headers.get(HttpHeaders.ACCEPT_LANGUAGE);
                //logger.debug("Get accept-language from client headers : {}", accepts);
                if (null != accepts) {
                    for (String accept : accepts) {
                        try {
                            //return localeExtractor.extractFromAccept(accept);
                            return new LocaleExtractor().extractFromAccept(accept);
                        } catch (LocaleNotFoundException ex) {
                            // TODO:
                        }
                    }
                }
            }
        }
        return Locale.US;
    }

    public static class LocaleExtractor {
        public Locale extractFromAccept(String accept) throws LocaleNotFoundException {
            if(null != accept) {
                Pattern pattern = Pattern.compile(".*(\\w\\w)-(\\w\\w).*");
                Matcher matcher = pattern.matcher(accept);
                if (matcher.matches()) {
                    return new Locale(matcher.group(1), matcher.group(2));
                }
            }
            throw new LocaleNotFoundException();
        }
    }
    public static class LocaleNotFoundException extends Exception{
        public LocaleNotFoundException(){}
    }
}
